#include "Sphere.h"

#define MAX_VECTOR 500

/*
 *	Sphere constructor
 */

Sphere::Sphere(Ogre::SceneManager *sceneManager, Ogre::String name, Material mat, Ogre::String texture, Ogre::Vector3 initPosition,  Ogre::Vector3 initAccel, Ogre::Vector3 size, Ogre::Real radius, bool collides)
{
	m_displacementVector = initAccel;
	m_scene = sceneManager;
	m_radius = radius;
	m_collides = collides;
	m_mat = mat;
	m_gravity = 6;
	m_previousPosition = initPosition;
	m_freeze = false;
	m_selected = false;
	m_initPosition = initPosition;

	switch(m_mat) {
	case IRON: 
		m_bounce = 0.6;
		m_friction = 0.4;
    break;
	case WOOD: 
    	m_bounce = 0.8;
		m_friction = 0.7;
    break;
	case MARBLE: 
    	m_bounce = 0.83;
		m_friction = 0.95;
    break;
	case RUBBER: 
    	m_bounce = 0.95;
		m_friction = 0.95;
    break;
	default: 
		m_bounce = 0.9;
		m_friction = 0.9;
	}

	// generate sphere mesh 
	Ogre::Entity* sphereMesh = m_scene->createEntity(name,"sphere.mesh");
	sphereMesh->setMaterialName(texture);
	sphereMesh->setCastShadows(true);

	// generate a new scene node, attach sphere mesh to it and adjust size and initial position
    m_node = m_scene->getRootSceneNode()->createChildSceneNode();
    m_node->attachObject(sphereMesh);
	m_node->scale(size);
	m_node->setPosition(initPosition);
}

/*
 *	Sphere  destructor
 */

Sphere::~Sphere(void){}

/*
 *	Sphere::move is called by World::worldStep and sets a new sphere position according to sphere displacement Vector and checks for bounding collision
 */

void Sphere::move(const Ogre::FrameEvent& evt)
{
	// apply gravity
	if(m_collides && !m_selected)
	{	
		m_displacementVector.y -= m_gravity;		
	}

	if(m_selected)
	{
		setAcceleration((m_mousePosition - getPosition())*5);
	}

	//limit speed (only temprary until collsion fix!)
	if(this->m_displacementVector.length() > MAX_VECTOR)
	{
		this->m_displacementVector.normalise();
		this->m_displacementVector *= MAX_VECTOR;
	}
	
	//debug freeze
	if(m_freeze)
	{
		m_displacementVector = Ogre::Vector3(0,0,0);
	}

	// save last postition
	m_previousPosition = this->getPosition();

	// adjust positiona ccording to displacement vector
	m_node->setPosition(m_node->getPosition()+(m_displacementVector * evt.timeSinceLastFrame));		//adjust positiona ccording to new vector}
}

/*
 *	Sphere::collisonCheck handles environment collisions (including frame lag compensation)
 */

void Sphere::collisionCheck()
{
	// get sphere position
	Ogre::Vector3 currPos = m_node->getPosition();
	
	// compute randomized collision noise
	Ogre::Real collisionNoise = (rand() % NOISE) - NOISE; 

	//detect world bound collisiosn
	if(m_node->getPosition().y < FLOOR && m_displacementVector.y <= 0)
	{
		m_node->setPosition(currPos.x,FLOOR,currPos.z);
		m_displacementVector.x *= m_friction;
		m_displacementVector.y *= -m_bounce;
	}
	else if(m_node->getPosition().y > TOP && m_displacementVector.y >= 0)
	{
		m_node->setPosition(currPos.x,TOP,currPos.z);
		m_displacementVector.x *= m_friction;
		m_displacementVector.y *= -m_bounce;
	}
	else if(m_node->getPosition().x < WORLD_EDGE_LEFT && m_displacementVector.x <= 0)
	{
		m_node->setPosition(WORLD_EDGE_LEFT,currPos.y,currPos.z);
		m_displacementVector.x *= -m_bounce;	
		m_displacementVector.y *= m_friction;
	}
	else if(m_node->getPosition().x > WORLD_EDGE_RIGHT && m_displacementVector.x >= 0)
	{
		m_node->setPosition(WORLD_EDGE_RIGHT,currPos.y,currPos.z);
		m_displacementVector.x *= -m_bounce;
		m_displacementVector.y *= m_friction;
	}
}
/*
 *	Sphere::collideWith handles collisions with other spheres by computing a collision force vector
 */

void Sphere::collideWith(Sphere *collider, const Ogre::FrameEvent& evt)
{
	// compute vector connecting sphere center points (vector from collider to self)
	Ogre::Vector2 center2Center;
		
	center2Center.x = this->getPosition().x - collider->getPosition().x;
	center2Center.y = this->getPosition().y - collider->getPosition().y;
	
	//compute force vector by projecting the displacement vector of the collider onto the center to center vector
	Ogre::Vector2 forceProjection = (((collider->m_displacementVector.x * center2Center.x) + 
									  (collider->m_displacementVector.y * center2Center.y))/
									  (center2Center.x * center2Center.x) + (center2Center.y * center2Center.y)) * center2Center;

	// adjust self dispalcement vector
	this->m_displacementVector.x = (this->m_displacementVector.x + (forceProjection.x * evt.timeSinceLastFrame * m_bounce));
	this->m_displacementVector.y = (this->m_displacementVector.y + (forceProjection.y * evt.timeSinceLastFrame * m_bounce));
}

